package cn.ilikes.tools.nosql.jpa.classloads.porxys;

import java.lang.reflect.Method;

import javax.persistence.PersistenceException;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.NoOp;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.ilikes.tools.nosql.jpa.clients.AbClient;
import cn.ilikes.tools.nosql.jpa.graphs.Node;
import cn.ilikes.tools.nosql.jpa.graphs.NodeLifeCycle.LifeCycle;
import cn.ilikes.tools.nosql.jpa.metadatas.EntityMetaData;
import cn.ilikes.tools.nosql.jpa.metadatas.elements.EntityFields;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.AbstractContext;

public class CglibLazyInitializer implements LazyInitializers, InvocationHandler {

	/** The Constant log. */
	private static final Log log = LogFactory.getLog(CglibLazyInitializer.class);

	private EntityFields entityFields;

	private EntityMetaData targetEntityMetaData;

	private Object keyId;

	private AbstractContext abstractContext;

	private final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
		public int accept(Method method) {
			if (method.equals(targetEntityMetaData.getEntityId().getSetMethod()) || method.getName().equals("finalize")
					|| method.equals(targetEntityMetaData.getEntityId().getGetMethod()))
				return 1;

			for (EntityFields entityFields : targetEntityMetaData.getEntityFields().values()) {
				if (method.equals(entityFields.getSetMethod()) || method.equals(entityFields.getGetMethod()))
					return 0;
			}
			return 1;
		}
	};

	/**
	 * 
	 * @param entityFields
	 * @param entityMetaData
	 * @param keyId
	 * @param client
	 */
	public CglibLazyInitializer(EntityFields entityFields, EntityMetaData targetEntityMetaData, Object keyId, AbstractContext abstractContext) {
		this.entityFields = entityFields;
		this.targetEntityMetaData = targetEntityMetaData;
		this.keyId = keyId;
		this.abstractContext = abstractContext;
	}

	@Override
	public Object getProxy() throws PersistenceException {
		try {
			Enhancer e = new Enhancer();
			e.setSuperclass(targetEntityMetaData.getEntityClazz());
			e.setInterfaces(targetEntityMetaData.getInterfaces());
			e.setCallbackTypes(new Class[] { InvocationHandler.class, NoOp.class });
			e.setCallbackFilter(FINALIZE_FILTER);
			e.setCallbacks(new Callback[] { this, NoOp.INSTANCE });
			e.setUseFactory(false);
			e.setInterceptDuringConstruction(false);
			Object object = e.create();
			Method method = targetEntityMetaData.getEntityId().getSetMethod();
			method.invoke(object, keyId);
			return object;
		} catch (Exception e) {
			throw new PersistenceException(e);
		}
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		Node node = abstractContext.getCache(keyId, targetEntityMetaData);
		if (node == null || !node.getNodeLifeCycle().isLifeCycle(LifeCycle.managed)) {
			AbClient client = abstractContext.getClient();
			node = client.findById(targetEntityMetaData, keyId);
			node.getNodeLifeCycle().setLifeCycle(LifeCycle.managed);
			abstractContext.putCache(keyId, node);
			//			Object  client.findValue(targetEntityMetaData, keyId, entityFields.getField().getType(), JPAUtils.getfamily(columnName, index), JPAUtils.getQualifier(columnName, index));	
			//			EntityFields entityFields = selectByMethod(method);
			//			String columnName = entityFields.getjPAField().getColumnName();
			//			int index = columnName.indexOf(SysConfigs.hbase_column_family_qualifier);

		}
		return method.invoke(node.getEntity(), args);

	}

	private EntityFields selectByMethod(Method method) throws Throwable {
		for (EntityFields entityFields : targetEntityMetaData.getEntityFields().values()) {
			if (method.equals(entityFields.getGetMethod()) || method.equals(entityFields.getSetMethod()))
				return entityFields;
		}

		throw new Throwable("not find method : " + method.getName() + " 's EntityFields ");
	}

}
